package it.antonio.ilpizze.db.core;

import it.antonio.ilpizze.db.Cache;
import it.antonio.ilpizze.db.Id;
import it.antonio.ilpizze.db.Record;
import it.antonio.ilpizze.db.cache.CacheDelegate;
import it.antonio.ilpizze.db.cache.CacheUnit;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;

public class TransactionalCache implements Cache {
	
	private TransactionManager txManager;
	private StatusManager statusManager;
	
	private CacheDelegate delegate;
	private List<CacheUnit> cacheUnits = new LinkedList<CacheUnit>();
	private Stack<CacheUnit> executedCacheUnits = new Stack<CacheUnit>();
	
	private Map<Id, Record> inTransactionRecords = new HashMap<Id, Record>();
	
	public TransactionalCache(CacheDelegate delegate, TransactionManager txManager, StatusManager statusManager) {
		this.txManager = txManager;
		this.statusManager = statusManager;
		this.delegate = delegate;
	}

	public void begin() {
		cacheUnits.clear();
		executedCacheUnits.clear();
	}

	public void commit() {
		while(!cacheUnits.isEmpty()){
			CacheUnit unit = cacheUnits.remove(0);
			unit.commit(delegate);
			executedCacheUnits.push(unit);
		}
		inTransactionRecords.clear();
	}

	public void rollback() {
		while(!executedCacheUnits.isEmpty()){
			CacheUnit unit = executedCacheUnits.pop();
			unit.rollback(delegate);
		}
		inTransactionRecords.clear();
	}

	@Override
	public void reset() {
		delegate.reset();
	}

	@Override
	public void put(Id id, Record record) {
		if(txManager.autoCommit() == false) {
			statusManager.checkStatus(Status.TRANSACTION);
			cacheUnits.add(CacheUnit.put(id, record));
			inTransactionRecords.put(id, record);
		} else {
			statusManager.checkStatus(Status.OPEN);
			delegate.put(id, record);
		}
	}

	@Override
	public Record remove(Id id) {
		if(txManager.autoCommit() == false) {
			statusManager.checkStatus(Status.TRANSACTION);
			Record r = inTransactionRecords.remove(id);
			cacheUnits.add(CacheUnit.remove(id));
			if(r != null){
				return r;
			} else {
				return delegate.get(id);
			}
		} else {
			statusManager.checkStatus(Status.OPEN);
			return delegate.remove(id);
		}
	}

	@Override
	public Record get(Id id) {
		Record r = inTransactionRecords.get(id);
		if(r != null){
			return r;
		}
		return delegate.get(id);
	}

}
